package se.kth.ict.id2203.assignment2;

import java.util.Set;

import org.apache.log4j.PropertyConfigurator;

import se.kth.ict.id2203.console.Console;
import se.kth.ict.id2203.console.java.JavaConsole;
import se.kth.ict.id2203.flp2p.FairLossPointToPointLink;
import se.kth.ict.id2203.flp2p.delay.DelayDropLink;
import se.kth.ict.id2203.flp2p.delay.DelayDropLinkInit;
import se.kth.ict.id2203.pb.LPB;
import se.kth.ict.id2203.pb.LPBInit;
import se.kth.ict.id2203.pb.ProbabilisticBroadcast;
import se.kth.ict.id2203.sub.SUB;
import se.kth.ict.id2203.sub.SUBInit;
import se.kth.ict.id2203.sub.UnreliableBroadcast;
import se.sics.kompics.Component;
import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Fault;
import se.sics.kompics.Handler;
import se.sics.kompics.Kompics;
import se.sics.kompics.address.Address;
import se.sics.kompics.launch.Topology;
import se.sics.kompics.network.Network;
import se.sics.kompics.network.mina.MinaNetwork;
import se.sics.kompics.network.mina.MinaNetworkInit;
import se.sics.kompics.timer.Timer;
import se.sics.kompics.timer.java.JavaTimer;


public class Assignment2Main extends ComponentDefinition {
	
	static {
		PropertyConfigurator.configureAndWatch("log4j.properties");
	}
	
	private static int selfId;
	private static String commandScript;
	
	Topology topology = Topology.load(System.getProperty("topology"), selfId);


	public static void main(String[] args) {
		
		selfId = Integer.parseInt(args[0]);
		commandScript = args[1];

		Kompics.createAndStart(Assignment2Main.class);
	}

	/**
	 * Instantiates a new assignment1 group0.
	 */
	public Assignment2Main() {
		// create components
		Component time = create(JavaTimer.class);
		Component network = create(MinaNetwork.class);
		Component con = create(JavaConsole.class);
		Component flp2p = create(DelayDropLink.class);
		Component app = create(Application2.class);
		Component pb = create(LPB.class);
		Component sub = create(SUB.class);

		// handle possible faults in the components
		subscribe(handleFault, time.control());
		subscribe(handleFault, network.control());
		subscribe(handleFault, con.control());
		subscribe(handleFault, flp2p.control());
		subscribe(handleFault, app.control());
		subscribe(handleFault, pb.control());
		subscribe(handleFault, sub.control());
		
		// initialize the components
		Address self = topology.getSelfAddress();
		Set<Address> neighborSet = topology.getNeighbors(self);

		trigger(new MinaNetworkInit(self, 5), network.control());
		trigger(new DelayDropLinkInit(topology, 0), flp2p.control());
		trigger(new Application2Init(commandScript, neighborSet, self), app
				.control());
		trigger(new SUBInit(topology), sub.control());
		
		// int fanout, double store_threshold, int maxrounds, long delta
		trigger(new LPBInit(topology, 2, 0, 2, 10000), pb.control());
		


		// connect the components
		connect(app.required(Console.class),
				con.provided(Console.class));
		
		connect(app.required(ProbabilisticBroadcast.class),
				pb.provided(ProbabilisticBroadcast.class));
		
		connect(app.required(Timer.class), 
				time.provided(Timer.class));

		connect(pb.required(UnreliableBroadcast.class),
				sub.provided(UnreliableBroadcast.class));
		
		connect(pb.required(FairLossPointToPointLink.class),
				flp2p.provided(FairLossPointToPointLink.class));
		
		connect(pb.required(Timer.class),
				time.provided(Timer.class));
				
		connect(sub.required(FairLossPointToPointLink.class),
				flp2p.provided(FairLossPointToPointLink.class));
		
		connect(flp2p.required(Timer.class), time.provided(Timer.class));
		connect(flp2p.required(Network.class), network.provided(Network.class));

	}

	Handler<Fault> handleFault = new Handler<Fault>() {
		public void handle(Fault fault) {
			fault.getFault().printStackTrace(System.err);
		}
	};
}
